import os
import random
import string
from datetime import datetime
import logging
import time
from typing import List, Literal, Optional, TypedDict

from backend.services.svc_host_machine_info import get_host_machine_info_by_id
from ..utils.docker_operator_client import DockerOperatorClient
from docker.models.containers import Container
from docker.types import EndpointConfig
from ..models import (
    ContainerInfoDict,
    DevEnvironment,
    DevEnvConfigDict,
    CreateContainerInfoDict,
    UserContainerLink,
)
from ..models.mdl_dev_env_app_cfgs import PROXY_TYPES
from ..configs import get_config
from .svc_dev_env_imgs import get_dev_env_img_by_id

# from .svc_nginx_proxy import register_dev_env_proxy
from .svc_dev_env_app_cfgs import get_app_cfg
from .svc_containers import link_container_and_user
from flask_login import current_user
from ..baseapp import db, nginx_manager, app
from ..utils.tasks_manager import Task


logger = logging.getLogger(__name__)


def get_dev_env_by_id(dev_env_id: int) -> Optional[DevEnvironment]:
    return DevEnvironment.query.filter_by(id=dev_env_id).first()


def get_dev_env_by_name(dev_env_name: str) -> Optional[DevEnvironment]:
    return DevEnvironment.query.filter_by(name=dev_env_name).first()


def get_all_containers(dev_env_name: str) -> list[ContainerInfoDict]:
    """
    Get all containers under a development environment
    """
    if dev_env_name is None:
        raise ValueError("dev_env_name cannot be None")

    # 获取开发环境信息
    dev_env = get_dev_env_by_name(dev_env_name)
    if not dev_env:
        raise ValueError(f"DevEnvironment not found for name {dev_env_name}")

    # 获取主机信息
    host_machine_info = get_host_machine_info_by_id(dev_env.host_machine_id)
    if not host_machine_info:
        raise ValueError(f"HostMachineInfo not found for id {dev_env.host_machine_id}")

    # 使用 DockerOperatorClient 获取主机上的所有容器
    docker_operator_client = DockerOperatorClient(host_machine_info.address)
    all_containers = docker_operator_client.list_containers(all_containers=True)

    # 获取与当前开发环境关联的容器 ID
    links = UserContainerLink.query.filter_by(dev_env_id=dev_env.id).all()
    linked_container_ids = {link.container_id for link in links}

    # 筛选出与 Links 相同的容器
    filtered_containers = [
        container
        for container in all_containers
        if container["id"] in linked_container_ids
    ]

    return filtered_containers


def generate_container_name(dev_env_name: str) -> str:
    """
    Generate container name for a development environment name
    """
    now = datetime.now()
    year = now.strftime("%Y")  # 四位数的年份
    month = now.strftime("%m")  # 两位数的月份
    day = now.strftime("%d")  # 两位数的日期

    # 生成2位随机字母和数字组合
    random_suffix = "".join(random.choices(string.ascii_letters + string.digits, k=2))

    # 拼接容器名称
    container_name = f"{dev_env_name}--{year}{month}{day}{random_suffix}"
    return container_name


class CreateDevEnvLeaderTask(Task):
    def __init__(
        self,
        task_id: str,
        host_machine_id: int,
        docker_operator_client: DockerOperatorClient,
        dev_env_img_id: int,
        dev_env_name: str,  # 开发环境名称，与当前活跃的容器名称相同。
        user_id: int,
        proxy_type: PROXY_TYPES,
        network: str,
        container_ip: str,
        port_mapping: dict[str, str],
    ):
        super().__init__(
            task_id,
        )
        self._status_html = []
        self.host_machine_id: int = host_machine_id
        self.docker_operator_client: DockerOperatorClient = docker_operator_client
        self.dev_env_img_id: int = dev_env_img_id
        self.dev_env_name: str = dev_env_name
        self.user_id: int = user_id
        self.proxy_type: PROXY_TYPES = proxy_type
        self.network: str = network
        self.container_ip: str = (
            container_ip  # if is empty string, means the ip is unset.
        )
        self.port_mapping = port_mapping

    def get_status_html(self) -> List[str]:
        return self._status_html

    def run(self):
        with app.app_context():
            self.main_task(
                self.host_machine_id,
                self.docker_operator_client,
                self.dev_env_img_id,
                self.dev_env_name,
                self.port_mapping,
            )

    def main_task(
        self,
        host_machine_id: int,
        docker_operator_client: DockerOperatorClient,
        dev_env_img_id: int,
        dev_env_name: str,  # 开发环境名称，与当前活跃的容器名称相同。
        port_mappings: dict[str, str],
        allow_docker_ops=False,
        virtual_network="",
    ) -> ContainerInfoDict:  # Added password parameter
        """
        开始创建一个新的开发环境，注意是新建一个任务来创建。

        TODO：这个函数还需要好好优化，需要将方便测试的内容抽取出来。
        """
        dev_env_img = get_dev_env_img_by_id(dev_env_img_id)
        if dev_env_img is None:
            raise ValueError(
                f"Development environment for id {dev_env_img_id} not found"
            )
        app_cfg = get_app_cfg(dev_env_img.editor_type, dev_env_img.img_tag)

        # Check if all ports are mapped
        ports_mapping_valid, err = app_cfg.verify_other_ports(port_mappings)
        if not ports_mapping_valid:
            self._status_html = [f"<p>{err}</p>"]
            raise ValueError(err)

        ports_mapping_template: dict[str, str] = {}
        if app_cfg.get_port() > 0:  # If this app has a webport to expose
            ports_mapping_template[f"{app_cfg.get_port()}/tcp"] = "FREE_RANDOM_EDITOR"
        ports_mapping_template.update(port_mappings)

        info_dict: CreateContainerInfoDict = {
            "image": dev_env_img.img_tag,
            "name": generate_container_name(dev_env_name),
            "environment": {},
            "ports": ports_mapping_template,
            "command": app_cfg.get_command(self.proxy_type, "/" + dev_env_name),
            "volumes": app_cfg.get_volumes(
                os.path.join(get_config()["shared_volume_dir"], dev_env_name)
            ),
        }
        if self.network:
            info_dict["network"] = self.network
            if self.container_ip:
                info_dict["networking_config"] = {
                    self.network: EndpointConfig("1.22", ipv4_address=self.container_ip)
                }
        assert len(ports_mapping_template) > 0

        create_container_ret = docker_operator_client.new_create_container_task(
            info_dict
        )
        if create_container_ret.get("task_id") is None:
            raise ValueError(f"Task ID is None")

        # 选定的随机端口映射关系
        random_ports_mapping: dict[str, int] = create_container_ret[
            "random_ports_mapping"
        ]
        # 新建一个端口映射表，如果ports_mapping_template的值在随机端口映射中存在，
        # 那么就使用随机端口映射中的值，否则使用ports_mapping_template的值
        new_ports_mapping = {}
        for key, value in ports_mapping_template.items():
            if value in random_ports_mapping:
                new_ports_mapping[key] = random_ports_mapping[value]
            else:
                new_ports_mapping[key] = value
        logger.info(f"random_ports_mapping: {random_ports_mapping}")
        result = None
        while True:
            status = docker_operator_client.get_create_container_task_status(
                create_container_ret["task_id"]
            )
            self._status_html = status["status_html"]
            logger.info(f"Got status of docker create task: {status}")
            if status["status"] == "finished":
                result = status["result"]["container_info"]
                break
            time.sleep(1)
        # TODO: 增加session信息。
        # 获取容器信息
        container_info: ContainerInfoDict = result
        print("container info", container_info)
        host_machine_ip = docker_operator_client.host_machine_address.split(":")[0]
        # 当存在对外暴露的编辑器端口，则创建对应的开发环境代理
        proxy_port = -1
        sub_route = ""
        if app_cfg.get_port() > 0:
            if self.proxy_type == "http-subroute":
                nginx_manager.register_dev_env_proxy(
                    dev_env_name,
                    app_cfg.get_proxy_pass_url_subroute(
                        host_machine_ip,
                        random_ports_mapping["FREE_RANDOM_EDITOR"],
                        dev_env_name,
                    ),
                )
                sub_route = f"/{dev_env_name}/"
            elif self.proxy_type == "http-dedicated-port":
                proxy_port = nginx_manager.register_additional_http_server(
                    app_cfg.get_proxy_pass_url_dedicated_port(
                        host_machine_ip,
                        random_ports_mapping["FREE_RANDOM_EDITOR"],
                        dev_env_name,
                    ),
                )
                sub_route = "/"
            else:
                raise NotImplementedError(
                    f"Proxy type {self.proxy_type} is not supported"
                )

        try:
            # 创建开发环境
            dev_env = DevEnvironment(
                name=dev_env_name,
                user_id=self.user_id,
                configs={
                    "ports": new_ports_mapping,
                    "volumes": container_info["volumes"],
                    "networks": container_info["network"],
                    "command": container_info["command"],
                    "environment": container_info["environment"],
                },
                ide_type=dev_env_img.editor_type,
                ide_port=random_ports_mapping.get("FREE_RANDOM_EDITOR", -1),
                host_machine_id=host_machine_id,
                proxy_configs={
                    "type": self.proxy_type,
                    "port": proxy_port,
                    "sub_route": sub_route,
                },
            )
            db.session.add(dev_env)
            db.session.flush()
            # 将容器和用户进行关联
            user_container_link = UserContainerLink(
                container_id=container_info["id"],
                user_id=self.user_id,
                dev_env_id=dev_env.id,
                port=random_ports_mapping.get("FREE_RANDOM_EDITOR", -1),
                host_machine_id=host_machine_id,
            )

            db.session.add(user_container_link)
            db.session.commit()
        except Exception:
            import traceback

            traceback.print_exc()
            db.session.rollback()

        return container_info


def create_container_under_dev_env(
    image_name: int,
    container_name: str,  # 新镜像的名称
    dev_env_name: str,  # 开发环境名称，与当前活跃的容器名称相同。
) -> tuple[dict, bool]:  # Added password parameter
    """
    在开发环境中创建新的容器
    不要求镜像环境位于开发环境镜像列表中
    """

    try:
        # # 获取旧容器的信息
        # container_info = docker_op_cli.get_container_info(container_name)
        # logger.info(f"Got container info: {container_info}")

        # 获取开发环境
        dev_env = get_dev_env_by_name(dev_env_name)
        if not dev_env:
            raise ValueError(f"DevEnvironment not found for id {dev_env_name}")

        # 获取开发环境所在的宿主机
        host_machine = get_host_machine_info_by_id(dev_env.host_machine_id)

        # 实例化 DockerOperatorClient 调用 API
        docker_op_cli = DockerOperatorClient(host_machine.address)

        # 启动新容器
        # new_container_name = generate_container_name(dev_env.name)
        new_container_info = docker_op_cli.create_container(
            image=image_name,
            name=container_name,
            command=dev_env.configs["command"],  # TODO: 这里应该允许自定义覆盖命令
            environment=dev_env.configs["environment"],
            ports=dev_env.configs["ports"],
        )

        # 创建新容器的
        new_container_link = UserContainerLink(
            container_id=new_container_info["id"],
            user_id=current_user.id,
            dev_env_id=dev_env.id,
            port=dev_env.ide_port,
            host_machine_id=host_machine.id,
        )

        db.session.add(new_container_link)
        db.session.commit()

        return {
            "message": f"Container created successfully: {image_name}",
            "new_container_id": new_container_info["id"],
        }, True
    except Exception as e:
        import traceback

        traceback.print_exc()

        return {"error": str(e)}, False
